package cn.joylau.code;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.maven.plugin.AbstractMojo;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;

/**
 * @goal make-win-service
 * @phase package
 */
public class WindowsServiceMojo extends AbstractMojo {
    /**
     * @parameter property="project.build.directory"
     * @required
     */
    private File targetDir;

    /**
     * @parameter property="project.basedir"
     * @required
     * @readonly
     */
    private File baseDir;
    /**
     * @parameter property="project.build.sourceDirectory"
     * @required
     * @readonly
     */
    private File sourceDir;
    /**
     * @parameter property="project.build.testSourceDirectory"
     * @required
     * @readonly
     */
    private File testSourceDir;

    /**
     * @parameter property="project.groupId"
     * @required
     */
    private String groupId;

    /**
     * @parameter property="project.artifactId"
     * @required
     */
    private String artifactId;

    /**
     * @parameter property="project.version"
     * @required
     */
    private String version;

    /**
     * @parameter property="project.description"
     */
    private String description;

    /**
     * @parameter property="arguments"
     */
    private String[] arguments;

    /**
     * @parameter property="vmOptions"
     */
    private String vmOptions;

    /**
     * @parameter property="programArguments"
     */
    private String programArguments;


    private static final InputStream EXE_FILE_URL = WindowsServiceMojo.class.getClassLoader().getResourceAsStream("service.exe");
    private static final InputStream XML_FILE_URL = WindowsServiceMojo.class.getClassLoader().getResourceAsStream("service.xml");
    private static final InputStream CONFIG_FILE_URL = WindowsServiceMojo.class.getClassLoader().getResourceAsStream("service.exe.config");
    private static final InputStream README_FILE_URL = WindowsServiceMojo.class.getClassLoader().getResourceAsStream("reamdme.txt");

    public void execute() {
        getLog().info("开始生成 Windows Service 必要的文件");
        try {
            /*创建文件夹*/
            File distDir = new File(targetDir, File.separator + "dist");
            if (distDir.exists()) {
                try {
                    FileUtil.del(distDir);
                } catch (IORuntimeException e) {
                    getLog().error("删除目录失败！请检查文件是否在使用");
                    e.printStackTrace();
                }
            }
            FileUtil.mkdir(distDir.getPath());
            File logDir = new File(distDir, File.separator + "logs");
            FileUtil.mkdir(logDir.getPath());


            if (README_FILE_URL != null) {
                FileOutputStream readmeFileWriter = new FileOutputStream(distDir + File.separator + "readme.txt");
                IoUtil.copy(README_FILE_URL, readmeFileWriter);
                readmeFileWriter.flush();
                readmeFileWriter.close();
                README_FILE_URL.close();
            }


            if (XML_FILE_URL != null) {
                FileOutputStream xmlFileWriter = new FileOutputStream(distDir + File.separator + getJarPrefixName() + ".xml");
                IoUtil.copy(XML_FILE_URL, xmlFileWriter);
                xmlFileWriter.flush();
                xmlFileWriter.close();
                XML_FILE_URL.close();
            }
            if (EXE_FILE_URL != null) {

                FileOutputStream exeFileWriter = new FileOutputStream(distDir + File.separator + getJarPrefixName() + ".exe");
                IoUtil.copy(EXE_FILE_URL, exeFileWriter);
                exeFileWriter.flush();
                exeFileWriter.close();
                EXE_FILE_URL.close();
            }

            if (CONFIG_FILE_URL != null) {
                FileOutputStream configFileWriter = new FileOutputStream(distDir + File.separator + getJarPrefixName() + ".exe.config");
                IoUtil.copy(CONFIG_FILE_URL, configFileWriter);
                configFileWriter.flush();
                configFileWriter.close();
                CONFIG_FILE_URL.close();
            }
            /*下载文件*/
            FileUtil.copy(new File(targetDir.getPath() + File.separator + getJarName()), new File(distDir, File
                    .separator + getJarName()), true);



            convert(new File(distDir.getPath() + File.separator + getJarPrefixName() + ".xml"));
            createBat(distDir, "install.bat", "install");
            createBat(distDir, "uninstall.bat", "uninstall");
            createBat(distDir, "start.bat", "start");
            createBat(distDir, "stop.bat", "stop");
            createBat(distDir, "restart.bat", "restart");

            getLog().info("正在制作压缩包....");
            String zipDir = targetDir.getPath() + File.separator + getJarPrefixName() + ".zip";
            ZipUtils.zip(distDir.getPath(), zipDir);

            getLog().info("正在清除临时文件....");
            FileUtil.del(distDir);
            getLog().info("制作完成，文件:" + zipDir);
        } catch (Exception e) {
            getLog().error("制作Windows Service 失败：", e);
        }
    }


    /**
     * 属性转化
     *
     * @param xmlFile xml文件
     */
    private void convert(File xmlFile) {
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(xmlFile);
            Element root = document.getRootElement();
            root.element("id").setText(artifactId);
            root.element("name").setText(getJarPrefixName());
            root.element("description").setText(null == description ? "暂无描述" : description);
            if (arguments.length > 0) {
                getLog().warn("arguments 参数设置已过期,参数配置可能不会生效,请分别设置 vmOptions 参数 和 programArguments 参数 [https://github.com/JoyLau/joylau-springboot-daemon-windows]");
            }
            String vm_options = StrUtil.isEmpty(vmOptions) ? " " : " " + vmOptions + " ";
            String program_arguments = StrUtil.isEmpty(programArguments) ? "" : " " + programArguments;
            root.element("arguments").setText(vm_options + "-jar " + getJarName() + program_arguments);
            saveXML(document, xmlFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存 XML 文件
     *
     * @param document 文档
     * @param xmlFile  xml文件
     */
    private void saveXML(Document document, File xmlFile) {
        try {
            XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream(xmlFile), StandardCharsets.UTF_8));
            writer.write(document);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param outDri   输出目录
     * @param fileName 文件名
     * @param text     命令文本
     */
    private void createBat(File outDri, String fileName, String text) {
        if (!outDri.exists()) {
            FileUtil.mkdir(outDri.getPath());
        }
        File file = new File(outDri, fileName);
        try (FileWriter w = new FileWriter(file)) {
            w.write("@echo off\n" +
                    "%1 mshta vbscript:CreateObject(\"Shell.Application\").ShellExecute(\"cmd.exe\",\"/c %~s0 ::\",\"\",\"runas\",1)(window.close)&&exit\n" +
                    "%~dp0" + getJarPrefixName() + ".exe " + text + "\n" +
                    "echo The " + getJarPrefixName() + " service current state:\n" +
                    "%~dp0" + getJarPrefixName() + ".exe status\n" +
                    "pause");
        } catch (IOException e) {
//            throw new MojoExecutionException("Error creating file ", e);
            e.printStackTrace();
        }
        // ignore
    }

    /**
     * 获取jar包前缀名
     *
     * @return String
     */
    private String getJarPrefixName() {
        return artifactId + "-" + version;
    }

    /**
     * 获取jar包全名
     *
     * @return String
     */
    private String getJarName() {
        return getJarPrefixName() + ".jar";
    }
}
